ZK 5.0 Component Imagepicker

From Documentation
DocumentationSmall Talks2009OctoberZK 5.0 Component Imagepicker
ZK 5.0 Component Imagepicker

Author
Jimmy Shiau, Engineer, Potix Corporation
Date
October 27, 2009
Version
Applicable to ZK 5.0 and later

Introduction

When we create an application such as an image manager for a blog we would always provide the user with an easy way to select images and move them from one category to another. We could use components such as checkboxes or radio components for the selection, but that doesn’t provide a very user friendly experience. In this small talk, I will introduce this new component for ZK 5. This component is a container in which you can place images. It is then possible to select images by clicking or dragging your mouse just as you would in Windows Explorer. This provides better user functionality and makes it much easier to select the items the user desires.

Live Demo

Let's have a look at the demo of the ZK Imagepicker.

The following code fragment demonstrates how to use the Imagepicker component

<imagepicker  itemWidth="100px" itemHeight="100" itemPadding="5" itemMargin="5" >
	<image src="/1.jpg" />
	<image src="/2.jpg" />				
</imagepicker>

You can place an image into imagepicker tag and set the height and width of it. The result can then be placed into a container such as a window.

Component Usage

Here is the Imagepicker's attribute specification table:

Attribute Usage Default Value
itemWidth Sets the width of the item "100" (unit is pixel)
itemHeight Sets the height of the item "100" (unit is pixel)
itemPadding Sets the padding of the item "5" (unit is pixel)
itemMargin Sets the margin of the item "5" (unit is pixel)

Event Listeners

The Imagepicker component provides one event. This event allows the user to execute code when an item of the Imagepicker is selected:

  1. onSelect: The event is triggered when a user selects an item.

Behind The Scene: How to Implement a ZK5 component

Firstly you can refer to ZK Component Development Essentials to read about ZK 5’s Basic Concepts.

A component class in Java

The component class must extend from AbstractComponent or one of its derived class. There are several skeletal implementations available and you can choose the appropriate one for your usage. For this component, we will use the XulElement,

Example.jpg

You can create a java class extends XulElement, then provide getter and setter methods for all attributes. The setter methods must be updated using smartUpdate.

public String getItemWidth() {
	return _itemwd;
}

public void setItemWidth(String itemwd) throws WrongValueException {
	if (itemwd != null && itemwd.length() == 0)
		itemwd = null;
	if (!Objects.equals(_itemwd, itemwd)) {
		_itemwd = itemwd;			
		smartUpdate("itemWidth", itemwd);
	}
}

Then we need to override the functions beforeChildAdded, service, renderProperties.

  1. beforeChildAdded: used to limit the type of the child.
  2. service: Define what to do on server side when event triggered, like “onSelect”, and you must register “onSelect” event.
  3. renderProperties: used to render all attribute
public void beforeChildAdded(Component newChild, Component refChild) {
	if (!(newChild instanceof Image)) 
		throw new UiException("Unsupported child for Datapicker: "+newChild);		
	super.beforeChildAdded(newChild, refChild);
}
	
static {		
	addClientEvent(Imagepicker.class, Events.ON_SELECT, 0);
}	

public void service(org.zkoss.zk.au.AuRequest request, boolean everError) {
	final String cmd = request.getCommand();
	if (cmd.equals(Events.ON_SELECT)) {
		Events.postEvent(SelectEvent.getSelectEvent(request));			
	} else {
		super.service(request, everError);
	}
}
	
protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer) throws IOException {
	super.renderProperties(renderer);
	if (!"100".equals(_itemwd))
		render(renderer, "itemWidth", _itemwd);
	if (!"100".equals(_itemhgh))
		render(renderer, "itemHeight", _itemhgh);
	if (!"5".equals(_itemPadding))
		render(renderer, "itemPadding", _itemPadding);
	if (!"5".equals(_itemMargin))
		render(renderer, "itemMargin", _itemMargin);	
}

Why renderProperties and smartUpdate?

A common question is why renderProperties (and redraw) are required to render value again when we notify the client with smartUpdate in setValue?

The simple answer is renderProperties is used to send all properties at once when a component is attached to the page at the first time. On the other hand, smartUpdate is used to send a property that was modified after the initial attachment.

A widget class in JavaScript

You can create javascript which extends zul.Widget, then define a setter and getter of all attributes by $define.

The $define function is a util function to declare set/get/is functions automatically

All attributes within the $define function will have set/get/is functions created automatically for them by zk.js

imagepicker.Imagepicker = zk.$extends(zul.Widget, {

	$define: {
		itemWidth : _zkf = function() {
			this.syncAttr();
		},
		itemHeight : _zkf,
		itemPadding : _zkf,
		itemMargin : _zkf
	},

	syncAttr: function() {
		if (!this.$n()) return;		
		
		//do realign
		
	},
  • _zkf is a temporary variable to cache a function

The function within $define will call syncAttr() to realign all images when some images have their values set.

The four class attributes will also call the same method (syncAttr()) after an attribute has a new value set.

You can then override the doClick_, doMouseOver_, and doMouseOut_ functions, to change the CSS of the target (div) when an event is triggered,

  1. doClick_ , doMouseOver_ , doMouseOut_: you can receive the clicked target by event.Target, or domTarget by event.domTarget, and change target’s css, then the function must be call this.$supers('doClick_', arguments) at last, doMouseOver_ and doMouseOut_ are the same with pervious one.
doClick_ : function(evt) {
	var target = evt.domTarget;
	//do xxx
	this.$supers('doClick_', arguments);
},	
doMouseOver_ : function(evt) {
	var target = evt.domTarget;
	//do xxx
	this.$supers('doMouseOver_', arguments);
},	
doMouseOut_ : function(evt) {
	var target = evt.domTarget;
	//do xxx
	this.$supers('doMouseOut_', arguments);
},

Regarding the Widget event, you can refer to Event.

After this we create a div as a selector, and monitor all edges of the selector, check the position of all items. If the edge of the selector touches any items, those items will be selected. We use this.fire(‘onSelect’) to notify the server side when an image is selected.

A JavaScript method to generate the DOM content (so-called mold)

The DOM content of a widget is called a mold. A widget can have several molds and each mold should be placed in an independent JavaScript file.

You can refer to SimpleLabel:Widgets Mold for more information on widget molds.

This component is created by div tags. We use div tags to define the container area, then enclose all children because we want the div to “frame” each image, and every div will change color (modify css) when some specific event is triggered.

function (out) {
	out.push('<div', this.domAttrs_(), '><div id="',
			this.uuid, '-cave"', 'class="', this.getZclass(),'-inner">');					
	for (var w = this.firstChild; w; w = w.nextSibling){
		out.push('<div class="', this.getZclass(),'-item">');
		w.redraw(out);
		out.push('</div>');
	}
	out.push('</div></div>');
}

domAttrs_ is a (protected) method inherited from zk.Widget. It returns all HTML attributes required, such as style, id and so on. You can override it if you want.


At previous widget class in JavaScript, it needs to override insertChildHTML_ and removeChildHTML_, used to enclose child by div tag, and remove div when child be remove,

	insertChildHTML_: function (child, before, desktop) {
		//enclose child by div
	},		
	removeChildHTML_: function (child, prevsib) {
		//distory div after it's child was be removed
	},

Add the component definition to lang-addon.xml (or lang.xml) (so-called Language Addon)

You can refer to SimpleLabel:Language Addon define your lang-addon.xml file.

Adds the widget definition to zk

Create a zk.wpd file to define your widget’s package. You can refer to SimpleLabel:Widget Package Descriptor for more information.

Debugging Setting

To debug your component you should change your settings in your zk.xml file to those displayed below:

<client-config>
	<debug-js>true</debug-js>
</client-config>

<library-property>
	<name>org.zkoss.web.classWebResource.cache</name>
	<value>false</value>
</library-property>

debug-js:

The debug-js attribute specifies whether to turn on the debugging of JavaScript files. By default, it is false and the compressed version of JavaScript files will be loaded which are hard to read and debug, though the footprint is much smaller.

To debug JavaScript files, you can set the attribute to true and the original uncompressed JavaScript files will be loaded instead.

org.zkoss.web.classWebResource.cache:

ZK, by default, allows the browser to cache static resources like images and JavaScript files. When you are developing a component, it is better to turn it off.

Download

Summary

The Imagepicker is an image container, creating a transparent frame for image selection. The component not only provides a click action for selecting images but a lasso effect similar to Windows Explorer, making it easy to use.

ZK 5 component operations can be split between the server-side and/or the client-side. The java class handles the server-side’s component tree and the javascript widget class handles the widget tree of client side. The widget tree will be create a dom tree in the browser and the component tree updates the widget tree by calling the methods renderProperties() or smartUpdate().



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.